package com.pig4cloud.pig.common.security.util;

import cn.hutool.core.map.MapUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.experimental.UtilityClass;
import org.springframework.security.oauth2.core.*;
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.endpoint.PkceParameterNames;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import java.io.IOException;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.Map;

/**
 * @author jumuning
 * @description OAuth2 端点工具
 */
@UtilityClass
public class OAuth2EndpointUtils {

	public final String ACCESS_TOKEN_REQUEST_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2";

	public MultiValueMap<String, String> getParameters(HttpServletRequest request) {
		Map<String, String[]> parameterMap = request.getParameterMap();
		MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>(parameterMap.size());
		parameterMap.forEach((key, values) -> {
			if (values.length > 0) {
				for (String value : values) {
					parameters.add(key, value);
				}
			}
		});
		return parameters;
	}

	public boolean matchesPkceTokenRequest(HttpServletRequest request) {
		return AuthorizationGrantType.AUTHORIZATION_CODE.getValue()
			.equals(request.getParameter(OAuth2ParameterNames.GRANT_TYPE))
				&& request.getParameter(OAuth2ParameterNames.CODE) != null
				&& request.getParameter(PkceParameterNames.CODE_VERIFIER) != null;
	}

	public void throwError(String errorCode, String parameterName, String errorUri) {
		OAuth2Error error = new OAuth2Error(errorCode, "OAuth 2.0 Parameter: " + parameterName, errorUri);
		throw new OAuth2AuthenticationException(error);
	}

	/**
	 * 格式化输出token 信息
	 * @param authentication 用户认证信息
	 * @param claims 扩展信息
	 * @return
	 * @throws IOException
	 */
	public OAuth2AccessTokenResponse sendAccessTokenResponse(OAuth2Authorization authentication,
			Map<String, Object> claims) {
//		todo bug修复
		Map<String, Object> mutableClaims = new HashMap<>();
		System.out.println(claims);
		if (MapUtil.isNotEmpty(claims)) {
			mutableClaims.putAll(claims); // 复制原有内容
		}


		OAuth2AccessToken accessToken = authentication.getAccessToken().getToken();
		OAuth2RefreshToken refreshToken = authentication.getRefreshToken().getToken();

		OAuth2AccessTokenResponse.Builder builder = OAuth2AccessTokenResponse.withToken(accessToken.getTokenValue())
			.tokenType(accessToken.getTokenType())
			.scopes(accessToken.getScopes());

		if(accessToken.getExpiresAt() != null){
			System.out.println(accessToken.getExpiresAt()); //注意这一个打印出来是UTC的时区，不是中国的时区，所以会慢，正常打印要转换时区
			//todo 修复bug 新增accesstoken expiresAt字段
			mutableClaims.put("expiresAt",accessToken.getExpiresAt().toEpochMilli());
		}
		if (accessToken.getIssuedAt() != null && accessToken.getExpiresAt() != null) {
			builder.expiresIn(ChronoUnit.SECONDS.between(accessToken.getIssuedAt(), accessToken.getExpiresAt())); //todo  这一个不太对，因为issueAt应该换成现在的时间，而不是token创建的时间
			//todo 修复bug 新增accesstoken 新增expiresIn字段
//			mutableClaims.put("expiresIn",ChronoUnit.SECONDS.between(accessToken.getIssuedAt(), accessToken.getExpiresAt())); //注意，这一个是秒级的时间 和前端别换算错了
		}


		if (refreshToken != null) {
			builder.refreshToken(refreshToken.getTokenValue());
		}

		if (MapUtil.isNotEmpty(claims)) {
//			immutableClaims = Collections.unmodifiableMap(mutableClaims); //使用 Collections.unmodifiableMap 将 mutableClaims 转换为不可修改的 Map。
//			builder.additionalParameters(claims); todo 修复bug 新增accesstoken expiresAt字段 新增expiresIn字段
			builder.additionalParameters(mutableClaims);
		}

//		todo 这个builder主要是为了 返回统一的tokenResponse格式，里面的值主要正确构建accessToken refreshToken,构建的expiresAt以accessToken的为准， expireseIN多久过期以build为准，build没有issueAttoken发布时间，所以他的expire是对的
//		System.out.println(builder.build().getAccessToken().getExpiresAt());
		return builder.build();
	}

}
